package atlantik.ui;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;

import modelz.AbstractUpdateableCollectionListener;
import modelz.UpdateListener;
import atlantik.game.AbstractAtlantikListener;
import atlantik.game.Atlantik;
import atlantik.game.AtlantikListenerDispatcher;
import atlantik.game.AtlantikState;
import atlantik.game.Auction;
import atlantik.game.Detail;
import atlantik.game.DetailAction;
import atlantik.game.Estate;
import atlantik.game.Game;
import atlantik.game.Player;

public class TextUI extends AbstractAtlantikListener implements Runnable {

	protected final Atlantik client;

	protected final AtlantikState state;

	protected final BufferedReader in;

	protected final PrintWriter out;

	private Thread thread;

	protected int playerId;

	public TextUI(Atlantik client, AtlantikListenerDispatcher dispatcher,
			Reader in, Writer out) {
		this.client = client;
		this.state = client.getState();
		this.in = new BufferedReader(in);
		this.out = new PrintWriter(out);

		dispatcher.addListener(this);
		state.players().addListener(new PlayerListener());
		state.games().addListener(new GameListener());
		state.auctions().addListener(new AuctionListener());
		state.details().addListener(new DetailListener());
		state.details().actions().addListener(new DetailActionListener());
	}

	public void chat(int playerId, String author, String message) {
		out.println((playerId == this.playerId ? " " : "") + author + ": "
				+ message);
		out.flush();
	}

	public void infoAdded(String msg) {
		out.println("INFO " + msg);
		out.flush();
	}

	public void errorAdded(String msg) {
		out.println("ERROR " + msg);
		out.flush();
	}

	public void start() {
		if (thread == null)
			(thread = new Thread(this)).start();
	}

	public void run() {
		while (Thread.currentThread() == thread) {
			try {
				String command = in.readLine();
				client.command(command);
			} catch (IOException e) {
				errorAdded("Retype please.");
			}
		}
	}

	public void serverVersion(String version) {
		infoAdded("Server version " + version);
	}

	public void clientInfo(int playerId, String cookie) {
		this.playerId = playerId;
	}

	public void newGameChoice(String gameType, String name, String description) {
		// TODO this method is no longer called
		infoAdded("Type .gn" + gameType + " to play " + name + " ("
				+ description + ").");
	}

	private class PlayerListener extends
			AbstractUpdateableCollectionListener<Player> {
		public void added(Player player) {
			infoAdded(player.getName() + " has joined.");
		}

		public void removed(Player player) {
			String name = player.getName();
			if( name == null )
				name = "Ghost from previous session";
			infoAdded(name + " left.");
		}
	}

	private class GameListener extends
	AbstractUpdateableCollectionListener<Game> implements
			UpdateListener<Game> {
		public void added(Game game) {
			game.addListener(this);
			game.options().addListener(new GameOptionListener());
			updated(game);
		}

		public void removed(Game game) {
			if (game == null)
				infoAdded("Game ended.");
			else
				infoAdded("Game " + game.getId() + " is no more.");
		}

		public void updated(Game game) {
			if (state.self().getGame() != null || !game.canBeJoined())
				return;

			String by = "";
			if (game.getMaster() != null)
				by = " by " + game.getMaster().getName();

			String allow = "";
			if (game.getMinPlayers() != 0 || game.getMaxPlayers() != 0)
				allow = " allowing " + game.getMinPlayers() + "-"
						+ game.getMaxPlayers() + " players,";

			infoAdded("Type .gj" + game.getId() + " to join " + game.getName()
					+ "(" + game.getDescription() + ")" + by + allow
					+ " having " + game.getPlayers() + " already");
		}
	}

	private class GameOptionListener extends
	AbstractUpdateableCollectionListener<Game.Option> implements
			UpdateListener<Game.Option> {
		public void added(Game.Option option) {
			option.addListener(this);
			updated(option);
		}
		public void updated(Game.Option option) {
			if (!option.canEdit() || !state.self().equals(state.self().getGame().getMaster()))
				return;
			if (option instanceof Game.BooleanValue) {
				Game.BooleanValue v = (Game.BooleanValue) option;
				boolean value = v.getValue();
				String state = (value ? "enabled" : "disabled");
				String targetState = (!value ? "enable" : "disable");
				String command = option.getId() + (!value ? "1" : "0");
				infoAdded(option.getTitle() + " is " + state + ". Type "
						+ command + " to " + targetState);
			}
		}
	}

	private class AuctionListener extends
	AbstractUpdateableCollectionListener<Auction> implements UpdateListener<Auction> {
		public void added(Auction auction) {
			Estate estate = auction.getEstate();
			infoAdded("Auctioning estate " + estate.getName() + " ("
					+ estate.getGroup().getName() + "), regular price "
					+ estate.getPrice());
			infoAdded("Type .ab" + auction.getId() + ":<amount> to bid.");
			auction.addListener(this);
		}

		public void removed(Auction auction) {
			infoAdded("Auction for " + auction.getEstate().getName()
					+ " ended.");
		}

		public void updated(Auction auction) {
			Auction.Bid highBid = auction.getHighBid();
			infoAdded(highBid.getBidder().getName() + " bid "
					+ highBid.getBid());
			if (auction.getStatus() != Auction.Status.NONE)
				infoAdded("For " + auction.getStatus());
		}
	}

	private class DetailListener extends
	AbstractUpdateableCollectionListener<Detail> {
		public void added(Detail detail) {
			if (detail.getEstate() != null)
				infoAdded("Estate " + detail.getEstate().getName() + ":");
			infoAdded(detail.getText());
		}
	}

	private class DetailActionListener extends
	AbstractUpdateableCollectionListener<DetailAction> {
		public void added(DetailAction action) {
			if (action.canPerform())
				infoAdded("Type " + action.getCommand() + " to "
						+ action.getCaption());
		}
	}

}
